import applyMixin from './mixin';
import ModuleCollection from './module/module-collection';
import { forEachValue } from './util';

export let Vue;

function getState(store, path) { // 获取最新的状态 可以保证视图更新
  return path.reduce((newState, current) => newState[current], store.state);
}

/**
 * @param {*} store 容器
 * @param {*} rootState 根模块
 * @param {*} path  所有路径
 * @param {*} module  我们刚刚格式化后的模块结构
 */
const installModule = (store, rootState, path, module) => {
  // 这里要对当前模块进行操作

  // 这里需要遍历当前模块上的actions、mutations、getters 都把他们定义在

  // 将所有子模块的状态安装到父模块上

  // 我要给当前订阅的事件 增加一个命名空间   a/changeAge b/changeAge a/c/changeAge
  const namespace = store._modules.getNameSpaced(path); // 返回前缀即可

  if (path.length > 0) { // vuex可以动态的添加模块
    const parent = path.slice(0, -1).reduce((memo, current) => memo[current], rootState);
    Vue.set(parent, path[path.length - 1], module.state);
  }

  module.forEachGetters((getter, key) => {
    store._wrapperGetters[namespace + key] = (state) => getter(state);
  });

  module.forEachMutations((mutation, key) => {
    store._mutations[namespace + key] = store._mutations[namespace + key] || [];
    store._mutations[namespace + key].push((payLoad) => {
      mutation.call(store, getState(store, path), payLoad);
      store._subscribes.forEach((fn) => {
        fn(store.state);
      });
    });
  });

  module.forEachActions((action, key) => {
    store._actions[namespace + key] = store._actions[namespace + key] || [];
    store._actions[namespace + key].push((payLoad) => {
      action.call(store, store, payLoad);
    });
  });

  module.forEachChild((child, key) => {
    // 递归加载模块
    installModule(store, rootState, path.concat(key), child);
  });
};

// 将状态和getters定义在vm实例上
const resetStoreVm = (store, state) => {
  const computed = {};
  store.getters = {};

  forEachValue(store._wrapperGetters, (fn, key) => {
    computed[key] = () => fn(state);
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key], // 去计算属性中取值
    });
  });

  store._vm = new Vue({
    data: {
      $$state: state,
    },
    computed,
  });
};

// vuex容器的初始化
export class Store {
  constructor(options) { // options 就是执行 new Vuex.store({state, mutation, actions})
    const { state } = options; // 数据变化更新视图(vue的核心依赖收集)

    this._actions = {};
    this._mutations = {};
    this._wrapperGetters = {};
    this._subscribes = [];

    // 数据的格式化 格式化成我想要的结果(树结构)
    // 1. 模块收集
    this._modules = new ModuleCollection(options);

    // 根模块的状态中，要将子模块通过模块名定义在根模块上
    // 2. 模块的格式化
    installModule(this, state, [], this._modules.root);

    // 3. 通过将状态和getters 都定义在当前的vm实例上
    resetStoreVm(this, state);

    // 插件内容会依次执行
    options.plugins.forEach((plugin) => plugin(this));
  }

  replaceState(state) {
    this._vm._data.$$state = state;
  }

  subscribe(fn) {
    this._subscribes.push(fn);
  }

  // vuex中的commit方法
  commit = (type, payload) => { // 保证当前this指向store实例
    // 调用commit其实就是去找刚才绑定好的mutation
    this._mutations[type].forEach((mutation) => {
      mutation.call(this, payload);
    });
  }

  // 5.actions的实现
  dispatch = (type, payload) => { // vuex中的dispatch方法
    this._actions[type].forEach((action) => {
      action.call(this, payload);
    });
  }
  // 6.模块的实现

  get state() {
    return this._vm._data.$$state;
  }
}

// Vue.use方法会调用插件的install方法，此方法中的参数就是Vue的构造函数
export const install = (_Vue) => { // vuex插件的安装 Vue.use()
  // _Vue 是Vue的构造函数
  Vue = _Vue;

  // 需要将根组件中注入的store 分派给每一个组件(子组件)
  applyMixin(Vue);
};
